package com.smart.service.impl;

import cn.hutool.core.collection.ListUtil;
import cn.hutool.core.date.DateField;
import cn.hutool.core.date.DateUtil;
import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.google.common.collect.Sets;
import com.smart.common.Constants;
import com.smart.common.PageInfo;
import com.smart.common.domain.BaseEntity;
import com.smart.common.enums.ConstructionStatusEnum;
import com.smart.common.enums.ErrorCode;
import com.smart.common.enums.ItemTypeEnum;
import com.smart.common.enums.OperateTypeEnum;
import com.smart.common.enums.ProjectTempTaskStatusEnum;
import com.smart.common.enums.ReportTypeEnum;
import com.smart.common.enums.SuperviseModuleEnum;
import com.smart.common.service.LogService;
import com.smart.common.utils.AssertUtil;
import com.smart.context.SessionContext;
import com.smart.mapper.JiansheProjectMapper;
import com.smart.mapper.ProjectMapper;
import com.smart.mapper.ProjectReportMapper;
import com.smart.mapper.ProjectTempTaskMapper;
import com.smart.model.convert.ProjectConvert;
import com.smart.model.convert.ProjectItemConvert;
import com.smart.model.convert.ProjectTempTaskConvert;
import com.smart.model.domain.ProjectDO;
import com.smart.model.domain.ProjectItemDO;
import com.smart.model.domain.ProjectReportDO;
import com.smart.model.domain.ProjectTempTaskDO;
import com.smart.model.dtomapper.ProjectTempTaskDtoMapper;
import com.smart.model.request.ProjectReportDetailRequest;
import com.smart.model.request.ProjectReportStatisticsRequest;
import com.smart.model.request.ProjectTempTaskAddRequest;
import com.smart.model.request.ProjectTempTaskRequest;
import com.smart.model.request.ProjectTempTaskUpdateRequest;
import com.smart.model.vo.ProjectReportDetailVO;
import com.smart.model.vo.ProjectTempTaskVO;
import com.smart.model.vo.ProjectVO;
import com.smart.service.ProjectItemService;
import com.smart.service.ProjectReportService;
import com.smart.service.ProjectService;
import com.smart.service.ProjectTempTaskService;
import com.smart.utils.BizCodeUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.tuple.Pair;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.text.MessageFormat;
import java.util.*;
import java.util.stream.Collectors;

/**
 * @author zhanghong
 */
@Slf4j
@Service
public class ProjectTempTaskServiceImpl extends ServiceImpl<ProjectTempTaskMapper, ProjectTempTaskDO> implements ProjectTempTaskService {
    @Autowired
    private ProjectTempTaskMapper projectTempTaskMapper;
    @Autowired
    private ProjectItemService projectItemService;
    @Autowired
    private ProjectReportMapper projectReportMapper;
    @Autowired
    private JiansheProjectMapper jiansheProjectMapper;
    @Autowired
    private ProjectMapper projectMapper;
    @Autowired
    private LogService logService;
    @Autowired
    private ProjectService projectService;
    @Autowired
    private ProjectReportService projectReportService;

    @Override
    public ProjectTempTaskVO getByKey(long id) {
        ProjectTempTaskDO projectTempTaskDO = getById(id);
        if(projectTempTaskDO == null) {
            return null;
        }
        ProjectTempTaskVO projectTempTaskVO = ProjectTempTaskDtoMapper.INSTANCE.do2VO(projectTempTaskDO);
        //查询关联的填报项目
        List<ProjectItemDO> projectItemDOS = projectItemService.listByTaskId(projectTempTaskDO.getId());
        projectTempTaskVO.setItems(ProjectItemConvert.buildProjectItemVoList(projectItemDOS));
        return projectTempTaskVO;
    }

    @Override
    public PageInfo<ProjectTempTaskVO> supervisePageQuery(ProjectTempTaskRequest request) {
        long count = projectTempTaskMapper.pageCount(request);
        List<ProjectTempTaskDO> list = projectTempTaskMapper.pageQuery(request, request.getPageSize(), request.fetchOffSet());
        List<ProjectTempTaskVO> projectTempTaskVOList = ProjectTempTaskConvert.buildProjectTempTaskVoList(list);
        fillInfo(projectTempTaskVOList, null);
        return new PageInfo(request.getPageNum(), request.getPageSize(), projectTempTaskVOList, count);
    }

    @Override
    public PageInfo<ProjectTempTaskVO> managePageQuery(ProjectTempTaskRequest request) {
        //查询关联的项目
        AssertUtil.assertNotNull(SessionContext.getJiansheUnitId(), ErrorCode.GUAN_LI_NOT_LOGIN_ERROR);
        List<Long> projects = jiansheProjectMapper.selectAllProjectId(SessionContext.getJiansheUnitId());
        if (CollectionUtils.isEmpty(projects)) {
            return PageInfo.buildEmpty();
        }

        //查询给项目下发过的临时提报任务集合
        LambdaQueryWrapper<ProjectReportDO> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(ProjectReportDO::getItemType, ReportTypeEnum.TEMP.name());
        queryWrapper.in(ProjectReportDO::getProjectId, projects);
        queryWrapper.orderByDesc(BaseEntity::getCreateTime);
        List<ProjectReportDO> projectReportDOS = projectReportMapper.selectList(queryWrapper);
        if (CollectionUtils.isEmpty(projectReportDOS)) {
            return PageInfo.buildEmpty();
        }

        //分页查询出对应的任务模板数据
        Set<Long> taskTempIds = projectReportDOS.stream().map(ProjectReportDO::getTaskId).collect(Collectors.toSet());
        //状态筛选,默认查询，进行中和已完成的
        List<String> statusList = Lists.newArrayList(ProjectTempTaskStatusEnum.RUNNING.name(), ProjectTempTaskStatusEnum.FINISH.name());
        if (StringUtils.isNotEmpty(request.getStatus())) {
            //指定状态查询
            statusList.clear();
            statusList.add(request.getStatus());
        }
        long count = projectTempTaskMapper.count(taskTempIds,
                statusList,
                request.getNameOrTaskNum(),
                request.getStartTime(),
                request.getEndTime());
        List<ProjectTempTaskDO> projectTempTaskDOS = projectTempTaskMapper.listPage(taskTempIds,
                statusList,
                request.getNameOrTaskNum(),
                request.getStartTime(),
                request.getEndTime(),
                request.getPageSize(),
                request.fetchOffSet());
        List<ProjectTempTaskVO> projectTempTaskVOList = ProjectTempTaskConvert.buildProjectTempTaskVoList(projectTempTaskDOS);

        // 数据填充
        fillInfo(projectTempTaskVOList, Sets.newHashSet(projects));

        return new PageInfo(request.getPageNum(), request.getPageSize(), projectTempTaskVOList, count);
    }

    @Override
    public List<ProjectTempTaskVO> listQuery(ProjectTempTaskRequest request) {
        List<ProjectTempTaskDO> list = lambdaQuery().eq(BaseEntity::getDeleted, 0).list();
        List<ProjectTempTaskVO> projectTempTaskVOS = ProjectTempTaskConvert.buildProjectTempTaskVoList(list);
        fillInfo(projectTempTaskVOS, null);
        return projectTempTaskVOS;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Boolean add(ProjectTempTaskAddRequest request) {
        AssertUtil.assertNotEmpty(request.getItems(), "请进行信息项配置");
        ProjectTempTaskDO projectTempTaskDO = ProjectTempTaskDtoMapper.INSTANCE.request2DO(request);
        Date endDate = DateUtil.endOfDay(request.getEndTime()).offset(DateField.SECOND, -1).toJdkDate();
        projectTempTaskDO.setEndTime(endDate);
        //解析任务编号
        if (StringUtils.isNotEmpty(request.getTaskNum())) {
            Pair<Date, Long> taskCodeInfo = BizCodeUtil.parseTaskCodeInfo(request.getTaskNum());
            projectTempTaskDO.setTaskCodeDate(taskCodeInfo.getLeft());
            projectTempTaskDO.setTaskCodeNum(taskCodeInfo.getRight().intValue());
        }

        boolean save = save(projectTempTaskDO);
        AssertUtil.assertTrue(save, "新增临时提报任务失败");
        List<ProjectItemDO> projectItemDOS = ProjectItemConvert.buildProjectAddItemDOList(request.getItems());
        projectItemDOS.forEach(item->{
            item.setTaskId(projectTempTaskDO.getId());
        });
        projectItemService.saveOrUpdateBatch(projectItemDOS);

        //是否是今天开始任务
        if (publishTask(request.getStartTime(), projectTempTaskDO)) {
            return true;
        }

        //记录操作日志
        String afterContent = JSON.toJSONString(getById(projectTempTaskDO.getId()));
        logService.logSupervisePlatformAddScene(SuperviseModuleEnum.临时任务, projectTempTaskDO.getId(), afterContent);
        return true;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Boolean updateByKey(ProjectTempTaskUpdateRequest request) {
        AssertUtil.assertNotEmpty(request.getItems(), "信息项不能为空");
        ProjectTempTaskDO projectTempTaskDO = ProjectTempTaskDtoMapper.INSTANCE.request2DO(request);
        boolean update = saveOrUpdate(projectTempTaskDO);
        AssertUtil.assertTrue(update, "更新临时提报任务失败");
        List<ProjectItemDO> projectItemDOS = ProjectItemConvert.buildProjectUpdateItemDOList(request.getItems());
        projectItemDOS.forEach(item->{
            item.setTaskId(projectTempTaskDO.getId());
        });
        // 查询原有的item列表
        List<ProjectItemDO> existItemList = projectItemService.lambdaQuery().eq(ProjectItemDO::getTaskId, projectTempTaskDO.getId()).list();
        if (CollectionUtils.isNotEmpty(existItemList)) {
            List<Long> existItemIdList = existItemList.stream().map(BaseEntity::getId).collect(Collectors.toList());
            List<Long> currItemIdList = projectItemDOS.stream().map(BaseEntity::getId).filter(Objects::nonNull).collect(Collectors.toList());
            Collection<Long> removeItemList = CollectionUtils.subtract(existItemIdList, currItemIdList);
            if (CollectionUtils.isNotEmpty(removeItemList)) {
                // 删除前端已删除的
                projectItemService.removeBatchByIds(removeItemList);
            }
        }
        // 更新或者新增item
        projectItemService.saveOrUpdateBatch(projectItemDOS);

        // 下发任务
        if (publishTask(request.getStartTime(), projectTempTaskDO)) {
            return true;
        }

        //记录操作日志
        String beforeContent = JSON.toJSONString(projectTempTaskDO);
        String afterContent = JSON.toJSONString(getById(projectTempTaskDO.getId()));
        logService.logSupervisePlatformUpdateScene(SuperviseModuleEnum.临时任务, projectTempTaskDO.getId(), beforeContent, afterContent);
        return true;
    }

    @Override
    public Boolean deleteByKey(long id) {
        projectTempTaskMapper.deleteDOById(id);
        return true;
    }

    @Override
    public List<ProjectTempTaskVO> listAlreadyStartByStatus(ProjectTempTaskStatusEnum status) {
        List<ProjectTempTaskDO> projectTempTaskDOS = projectTempTaskMapper.listAlreadyStartByStatus(status.name());
        return ProjectTempTaskConvert.buildProjectTempTaskVoList(projectTempTaskDOS);
    }

    @Override
    public PageInfo<ProjectVO> statisticProjectReport(ProjectReportStatisticsRequest request) {
        // 前置校验
        ProjectTempTaskDO taskDO = projectTempTaskMapper.selectDOById(request.getId());
        if (taskDO == null) {
            log.info("任务不存在");
            return PageInfo.buildEmpty();
        }
        request.setTaskId(taskDO.getId());

        // 建设单位只能看到当前所关联项目的提报数据
        if (SessionContext.getJiansheUnitId() != null) {
            List<Long> relationProjectList = jiansheProjectMapper.selectAllProjectId(SessionContext.getJiansheUnitId());
            if (CollectionUtils.isEmpty(relationProjectList)) {
                log.info("当前建设单位未关联任何项目");
                return PageInfo.buildEmpty();
            }
            request.setProjects(relationProjectList);
        }

        List<ProjectDO> projects = projectReportMapper.listEffectProjectByCondition(request);

        // 内存分页
        int count = projects.size();
        List<ProjectDO> page = ListUtil.page(request.getPageNum() - 1, request.getPageSize(), projects);
        List<ProjectVO> projectVOS = ProjectConvert.buildProjectVoList(page);
        return PageInfo.build(request.getPageNum(), request.getPageSize(), projectVOS, count, getExtInfoMap(taskDO.getId(), request.getProjects()));
    }

    @Override
    public ProjectReportDetailVO projectReportDetail(ProjectReportDetailRequest request) {
        ProjectTempTaskDO taskDO = projectTempTaskMapper.selectDOById(request.getId());
        AssertUtil.assertNotNull(taskDO, "临时提报任务不存在");

        ProjectDO projectDO = projectMapper.selectDOById(request.getProjectId());
        AssertUtil.assertNotNull(projectDO, "项目任务不存在");

        ProjectReportDO projectReportDO = projectReportMapper.queryByProjectIdAndTaskId(projectDO.getId(), taskDO.getId());
        AssertUtil.assertNotNull(projectReportDO, "临时提报任务记录不存在");

        ProjectReportDetailVO detailVO = new ProjectReportDetailVO();
        detailVO.setProjectName(projectDO.getProjectName());
        detailVO.setEngineeringType(projectDO.getEngineeringType());
        detailVO.setProjectNumber(projectDO.getProjectNumber());
        detailVO.setStreetCode(projectDO.getStreetCode());
        detailVO.setAddress(projectDO.getAddress());
        detailVO.setQualityRegistrationNumber(projectDO.getQualityRegistrationNumber());
        detailVO.setShigongUnit(projectDO.getShigongUnit());

        detailVO.setName(taskDO.getName());
        detailVO.setTaskNum(taskDO.getTaskNum());

        detailVO.setContent(projectReportDO.getContent());
        return detailVO;
    }

    @Override
    public String getTempTaskCode() {
        //查询获取下一个临时提报任务编号
        ProjectTempTaskDO recentOne = projectTempTaskMapper.getRecentOne();
        if (recentOne == null || !DateUtil.isSameDay(recentOne.getTaskCodeDate(), new Date())) {
            String yyMMdd = DateUtil.format(new Date(), "yyMMdd");
            return MessageFormat.format(Constants.TEMP_TASK_CODE_TEMPLE, yyMMdd, Constants.TEMP_TASK_CODE_START_NUM);
        }
        String yyMMdd = DateUtil.format(recentOne.getTaskCodeDate(), "yyMMdd");
        return MessageFormat.format(Constants.TEMP_TASK_CODE_TEMPLE, yyMMdd, recentOne.getTaskCodeNum() + Constants.TEMP_TASK_CODE_START_NUM);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Boolean revokeByKey(long id) {
        ProjectTempTaskDO tempTask = getById(id);
        AssertUtil.assertNotNull(tempTask, "数据不存在");

        tempTask.setStatus(ProjectTempTaskStatusEnum.REVOKE.name());
        saveOrUpdate(tempTask);

        //删除未提报的临时任务实例
        projectReportMapper.deleteUnReportInstance(tempTask.getId());

        //记录操作日志
        String beforeContent = JSON.toJSONString(tempTask);
        String afterContent = JSON.toJSONString(getById(id));
        logService.logSupervisePlatform(SuperviseModuleEnum.临时任务, OperateTypeEnum.修改, id, beforeContent, afterContent, "撤销");
        return true;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Boolean cancelByKey(long id) {
        ProjectTempTaskDO tempTask = getById(id);
        AssertUtil.assertNotNull(tempTask, "数据不存在");
        tempTask.setStatus(ProjectTempTaskStatusEnum.CANCEL.name());
        saveOrUpdate(tempTask);

        //记录操作日志
        String beforeContent = JSON.toJSONString(tempTask);
        String afterContent = JSON.toJSONString(getById(id));
        logService.logSupervisePlatform(SuperviseModuleEnum.临时任务, OperateTypeEnum.修改, id, beforeContent, afterContent, "作废");
        return true;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean createTempTask(Long projectId) {
        //扫描所有到期但是未开始的临时提报任务
        List<ProjectTempTaskVO> taskList = listAlreadyStartByStatus(ProjectTempTaskStatusEnum.RUNNING);
        if (CollectionUtils.isEmpty(taskList)) {
            log.info("临时提报任务为空...");
            return true;
        }
        //扫描所有"在施"或者"完工待验"的项目
        List<ProjectVO> projects = projectService.listProjectByStatus(Lists.newArrayList(ConstructionStatusEnum.RUNNING.getCode(), ConstructionStatusEnum.WAIT_ACCEPT.getCode()));
        if (CollectionUtils.isEmpty(projects)) {
            log.info("不存在-在施/完工待验的项目...");
            return true;
        }
        //为对应的项目生成临时提报任务单
        List<ProjectReportDO> entityList = Lists.newArrayList();
        for (ProjectTempTaskVO tempTaskVO : taskList) {
            ProjectReportDO reportDO = new ProjectReportDO();
            reportDO.setProjectId(projectId);
            reportDO.setTaskId(tempTaskVO.getId());
            reportDO.setItemType(ReportTypeEnum.TEMP.name());
            entityList.add(reportDO);
        }
        for (ProjectReportDO item : entityList) {
            boolean exists = projectReportService.lambdaQuery().eq(ProjectReportDO::getProjectId, item.getProjectId()).eq(ProjectReportDO::getTaskId, item.getTaskId()).exists();
            if (!exists) {
                projectReportService.save(item);
            }
        }
        return true;
    }

    /**
     * 获取统计信息
     * @param taskId
     * @param projects
     * @return
     */
    private Map<String, Object> getExtInfoMap(Long taskId, List<Long> projects) {
        Set<Long> projectSet = Sets.newHashSet();
        if (projects != null) {
            projectSet = Sets.newHashSet(projects);
        }
        //已提报数据
        Long reportNum = projectReportMapper.reportCountRangeProject(taskId, projectSet);
        //未提报数据[也只统计，在施和完工待验的项目数据]
        Long unReportNum = projectReportMapper.unReportCountRangeProject(taskId, projectSet);
        Map<String, Object> extInfo = Maps.newHashMap();
        extInfo.put(Constants.KEY_SHOULD_REPORT_NUM, reportNum + unReportNum);
        extInfo.put(Constants.KEY_UN_REPORT_NUM, unReportNum);
        return extInfo;
    }

    /**
     * 填充数据信息
     * @param projectTempTaskVOList
     * @param projects
     */
    private void fillInfo(List<ProjectTempTaskVO> projectTempTaskVOList, Set<Long> projects) {
        for (ProjectTempTaskVO tempTask : projectTempTaskVOList) {
            //填报项统计
            Long count = projectItemService.lambdaQuery().eq(ProjectItemDO::getTaskId, tempTask.getId()).count();
            tempTask.setItemCount(count);
            //已提报数据
            tempTask.setReportCount(projectReportMapper.reportCountRangeProject(tempTask.getId(), projects));
            //未提报数据[也只统计，在施和完工待验的项目数据]
            tempTask.setUnReportCount(projectReportMapper.unReportCountRangeProject(tempTask.getId(), projects));
        }
    }

    /**
     * 下发任务
     * @param request
     * @param projectTempTaskDO
     * @return
     */
    private boolean publishTask(Date request, ProjectTempTaskDO projectTempTaskDO) {
        //是否是今天开始任务
        if (request.before(DateUtil.endOfDay(new Date()))) {
            //立即下发任务
            //扫描所有"在施"或者"完工待验"的项目
            List<ProjectVO> projects = projectService.listProjectByStatus(Lists.newArrayList(ConstructionStatusEnum.RUNNING.getCode(), ConstructionStatusEnum.WAIT_ACCEPT.getCode()));
            if (CollectionUtils.isEmpty(projects)) {
                log.info("不存在-在施/完工待验的项目...");
                return true;
            }
            //为对应的项目生成临时提报任务单
            List<ProjectReportDO> list = Lists.newArrayList();
            for (ProjectVO project : projects) {
                ProjectReportDO reportDO = new ProjectReportDO();
                reportDO.setProjectId(project.getId());
                reportDO.setTaskId(projectTempTaskDO.getId());
                reportDO.setItemType(ReportTypeEnum.TEMP.name());
                list.add(reportDO);
            }
            projectReportService.saveBatch(list);
            //将临时提报任务标记为进行中
            projectTempTaskDO.setStatus(ProjectTempTaskStatusEnum.RUNNING.name());
            saveOrUpdate(projectTempTaskDO);
        }
        return false;
    }

    @Override
    public String fileType(long id) {
        ProjectTempTaskDO projectTempTaskDO = getById(id);
        if(projectTempTaskDO == null) {
            return null;
        }
        //查询关联的填报项目
        List<ProjectItemDO> projectItemDOS = projectItemService.listByTaskId(projectTempTaskDO.getId());
        long count = projectItemDOS.stream().filter(item -> Objects.equals(item.getItemType(), ItemTypeEnum.IMAGE.name())).count();
        if (count > 0) {
            return "ZIP";
        }
        return "EXCEL";
    }
}
